package com.yc154.web;

import com.yc.damai.entity.Promotion;
import com.yc.damai.entity.PromotionDetail;
import com.yc.damai.entity.User;
import com.yc.damai.vo.Result;
import com.yc154.dao.ProductDao;
import com.yc154.dao.PromotionDao;
import com.yc154.dao.PromotionDetailDao;
import com.yc154.vo.DacuOrder;
import com.yc154.web.fegin.UserDo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("promotion")
public class PromotionDo {

    private Promotion current;
    @Autowired
    private PromotionDao promotionDao;
    @Autowired
    private PromotionDetailDao promotionDetailDao;
    @Autowired
    private ProductDao productDao;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Transactional
    @RequestMapping("start")
    public Mono<Result> start() {
//        阻塞式代码
//        List<Promotion> list = promotionDao.findCurrent();
//        if(list == null || list.isEmpty()){
//            return Result.fail("当前没有大促活动!");
//        } else {
//            current = list.get(0);
//            current.setStatus("已开始");
//            promotionDao.save(current);
//        }
        // 响应式代码
        return promotionDao.findCurrentByStatus("未开始")
                .collectList()
                .flatMap(list -> {
                    if (list == null || list.isEmpty()) {
                        return Mono.just(Result.fail("当前没有大促活动!"));
                    } else {
                        current = list.get(0);
                        current.setStatus("已开始");
                        System.out.println("================ 启动大促活动: " + current.getTitle());
                        // 该方法一值报错
                        // promotionDao.save(current);
                        // 当前大促活动所有商品详情
                        List<PromotionDetail> details = new ArrayList<>();
                        return promotionDetailDao.findByProid(current.getId())
                                // 收集为List<PromotionDetail>
                                .collectList()
                                // 处理成功后返回结果
                                .doOnNext(ds -> {
                                    // 保存促销商品详情至details集合
                                    details.addAll(ds);
                                    // 将促销商品存入redis
                                    redisTemplate.opsForValue().set("promotionDetail", details);
                                    // 将每个商品pid+nums单独保存
                                    for (PromotionDetail detail : details) {
                                        redisTemplate.opsForValue().set("pid_" + detail.getPid(), detail.getNums());
                                    }
                                })
                                // 将detils集合中的nums属性提取出来, 收集为一个List<Integer>
                                .map(ds ->
                                        // stream流
                                        ds.stream()
                                                // 提取nums属性
                                                .map(PromotionDetail::getPid)
                                                // 收集为List<Integer>
                                                .collect(Collectors.toList()))
                                .flatMap(pids -> productDao.selectPromotionProducts(pids)
                                        .collectList()
                                        .doOnNext(products -> {
                                            redisTemplate.opsForValue().set("promotionProducts", products);
                                        })
                                        .thenReturn(pids))
                                // 优化更新: 批量更新商品数量 执行一个SQL完成所有商品数量的更新
                                .flatMap(pids -> productDao.updateNumsByPidIn(pids)
                                        // 成功后返回"OK", 尽管update定义返回结果, 但仍然为Mono<Void> 这是个坑
                                        .thenReturn("更新商品库存数 OK"))
                                // 更新促销状态
                                .flatMap(v -> promotionDao.updateStatus(current.getId(), current.getStatus())
                                        .thenReturn("更新促销状态 OK"))
                                // 成功后返回结果
                                .map(v -> Result.ok("大促活动开始了: " + current.getTitle()));
                    }
                });
    }

    @Transactional
    @RequestMapping("end")
    public Mono<Result> end() {
        if (current == null) {
            return Mono.just(Result.fail("活动未开始"));
        } else {
            System.out.println("================ 结束大促活动: " + current.getTitle());
            current.setStatus("已结束");
            return promotionDao.updateStatus(current.getId(), current.getStatus())
                    .thenReturn("大促活动结束")
                    .flatMap(msg -> {
//                        List<PromotionDetail> details = (List<PromotionDetail>) redisTemplate.opsForValue().get("promotionDetail");
//                        for (PromotionDetail detail : details) {
//                            // 恢复商品库存
//                            productDao.updateNumsByPid(detail.getPid(), detail.getNums()).subscribe();
//                        }
                        current = null;
                        // 清空redis中的促销商品详情
                        redisTemplate.delete("promotionDetail");
                        redisTemplate.delete("promotionProducts");
                        // 删除每个商品pid+nums
                        redisTemplate.keys("pid_*").forEach(key -> {
                            int pid = Integer.parseInt(key.substring(4));
                            // 恢复商品库存
                            productDao.updateNumsByPid(pid, (int) redisTemplate.opsForValue().get(key)).subscribe();
                            redisTemplate.delete(key);
                        });
                        // 还原 ready 初始值
                        ready = false;
                        return Mono.just(Result.ok("大促活动结束!"));
                    });
        }
    }

    @Transactional
    @RequestMapping("current")
    public Mono<Result> getCurrent() {
        if (current == null) {
            return Mono.just(Result.fail("活动未开始"));
        } else {
            return Mono.just(Result.ok(current.getTitle(), current));
        }
    }

    // TODO 如何实现分页查询
    // 查询促销商品信息
    @RequestMapping("getPromotionDetails")
    public Mono<Result> getPromotionDetails() {
        if (current == null) {
            return Mono.just(Result.fail("活动未开始"));
        } else {
            return Mono.just(Result.ok("当前促销商品",
                    redisTemplate.opsForValue().get("promotionProducts")));
        }
    }

    @PostConstruct
    public void init() {
        // 恢复当前(已开始)促销活动
        promotionDao.findCurrentByStatus("已开始")
                .collectList()
                .doOnNext(list -> {
                    if (list != null && list.isEmpty() == false) {
                        current = list.get(0);
                    }
                })
                // 订阅
                .subscribe(System.out::println);
    }

    @Autowired
    TaskScheduler taskScheduler;

    // 活动准备中...
    boolean ready = false;

    @Scheduled(cron = "0 * * * * *")
    public void autoStart(){
        if(ready){
            System.out.println("================ 大促活动准备中...");
            return;
        }
        promotionDao.selectByBegin()
                .collectList()
                .subscribe(list->{
                    if(list != null && list.isEmpty() == false){
                        System.out.println("================ 大促活动定时任务: " + list);
                        Promotion promotion = list.get(0);
                        // 启动延时任务, 指定在 begin 的时间点启动任务
                        taskScheduler.schedule(()->start().subscribe(),
                                Date.from(promotion.getBegin().atZone(ZoneId.systemDefault()).toInstant()));
                        System.out.println("================ begin: " + Date.from(promotion.getBegin().atZone(ZoneId.systemDefault()).toInstant()));
                        taskScheduler.schedule(()->end().subscribe(),
                                Date.from(promotion.getEnd().atZone(ZoneId.systemDefault()).toInstant()));
                        System.out.println("================ end: " + Date.from(promotion.getEnd().atZone(ZoneId.systemDefault()).toInstant()));
                        ready = true;
                    } else {
                        System.out.println("================ 当前没有大促活动!");
                    }
                });

    }

    @Autowired
    UserDo userDo;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @RequestMapping("commit")
    public Mono<Result> commit(int pid, @RequestHeader("Authorization") String auth){
        System.out.println("auth = " + auth);
        // 判断活动是否开始
        if(current == null || current.getBegin().isAfter(LocalDateTime.now())){
            return Mono.just(Result.fail("活动未开始!"));
        }
        return WebClient.create("http://localhost")
                .get()
                .uri("/auth/myinfo")
                .header("Authorization", auth)
                .retrieve()
                .bodyToMono(Result.class)
                .flatMap(result->{
                    if(result.getCode() != 1){
                        // 未登录
                        return Mono.just(result);
                    }
//                    int uid = ((User) result.getData()).getUid();
                    Map myinfo = ((Map) result.getData());
                    int uid = Integer.parseInt(myinfo.get("uid").toString());
                    // 判断商品数量
                    Integer nums = Integer.parseInt(redisTemplate.opsForValue().get("pid_" + pid).toString());
                    if(nums <= 0){
                        return Mono.just(Result.fail("商品库存不足!"));
                    }
                    // 扣减库存(redis) 事务, 默认每次秒杀只能是1件商品
                    redisTemplate.opsForValue().set("pid_" + pid, --nums);
                    // 向消息队列发送订单消息 => 消费者方法生成订单
                    // 发送消息到队列
                    rabbitTemplate.convertAndSend("dacu-order",
                            new DacuOrder(uid, pid, 1));
                    // 返回结果(告知用户订单已经受理)
                    return Mono.just(Result.ok("订单受理成功!"));
                });
        // 获取用户id (请求体参数: Auth...) 远程调用获取
//        Result result = userDo.myinfo();
//        if(result == null || result.getCode() != 1 ){
//            return Mono.just(result);
//        }
//        int uid = ((User) result.getData()).getUid();
//        System.out.println("------------- uid = " + uid);

//        return Mono.just(Result.ok("ok " + uid));
    }



}
